var
    FIELD_OFFSET_X = 2,
    FIELD_OFFSET_Y = 32,
    FIELD_WIDTH = 10,
    FIELD_HEIGHT = 20,	
    BLOCK_WIDTH = 20,
    BLOCK_HEIGHT = 20,
    MAX_SPEED = 9,
    LEVEL_TRESHOLDS = [1000, 2000, 3000, 4000, 5000, 6000, 7000, 8000],
    POINTS_FOR_DROPED_LINES = [0,100,250,425,750],
    canvas = null,
    ctx = null,
    O = "O",
    Z = "Z",
    S = "S",
    I = "I",
    J = "J",
    L = "L",
    T = "T",
    points = 0,
    speed = 1,
    X = 1,
    o = 0,
    gfx = null,
    tetrominos = {
        O: [[[X,X],[X,X]]],
        I: [[[X,X,X,X]],[[X],[X],[X],[X]]],	
        Z: [[[X,X,o],[o,X,X]],[[o,X],[X,X],[X,o]]],
        S: [[[o,X,X],[X,X,o]],[[X,o],[X,X],[o,X]]],	
        J: [[[X,X,X],[o,o,X]],[[X,X],[X,o],[X,o]],[[X,o,o],[X,X,X]],
                                               [[o,X],[o,X],[X,X]]],
        L: [[[X,X,X],[X,o,o]],[[X,X],[o,X],[o,X]],[[o,o,X],[X,X,X]],
                                               [[X,o],[X,o],[X,X]]],
        T: [[[X,X,X],[o,X,o]],[[X,o],[X,X],[X,o]],[[o,X,o],[X,X,X]],
                                               [[o,X],[X,X],[o,X]]]
    };
    field = [],
    tetromino = {
        name: T,
        rotation: 0,
        x: 0,
        y: 0
    },
    GS_OFF = 1,
    GS_ON = 2,
    GS_GAME_A = 4,
    GS_GAME_B = 8,
    GS_PAUSE = 16,
    GS_GAME_OVER = 32,

    game = GS_OFF;

function createRow(rowNr) {
    var 
        i,
        row = [],
        rowNr = arguments.length == 1 ? arguments[0] : 0;

    for (i = 0; i < FIELD_WIDTH + 2; i++)
        row[i] = (i === 0 || i === FIELD_WIDTH + 1 || 
                               rowNr === FIELD_HEIGHT) ? X : o;
    return row;
}

function resetField() {
    var row, col;
    for (row = 0; row <= FIELD_HEIGHT; row++)
        field[row] = createRow(row);
}

resetField();

var BREAK = -999;
function iterateOver(table, callback) {
	var
        rowOffset = arguments.length >= 3 ? arguments[2] : [0,0],
        colOffset = arguments.length >= 4 ? arguments[3] : [0,0],
        row, col;

	for (row = rowOffset[0]; row < table.length + rowOffset[1]; row++) {
        for (col = colOffset[0]; col < table[row].length + colOffset[1];
                                                                col++) {
            if (BREAK === callback(table[row][col], row, col)) 
                return;
        }
    }
}

function dropFullLines() {
	var row, col, linesRemoved = 0;
	for (row = 0; row < FIELD_HEIGHT; row++) {
		for (col = 0; col < FIELD_WIDTH; col++) {
			if (field[row][col] != X)
				break;
		}
		if(col == FIELD_WIDTH) {
			field.splice(row,1);
			field.unshift(createRow());
			linesRemoved++;
		}
	}
	return linesRemoved;
}

function drawSprite(name, x, y) {
    switch(name) {
		case 'empty_digit':
            ctx.drawImage(gfx, 0, 0, 12, 20, x, y, 12, 20);
            break;
        case 0:
        case 1:
        case 2:
        case 3:
        case 4:
        case 5:
        case 6:
        case 7:
        case 8:
        case 9:
            ctx.drawImage(gfx, 12*(parseInt(name,10)+1), 0, 12, 20, x, y,
                                                                    12, 20);

            break;
        case 'empty_block':
            ctx.drawImage(gfx, 20, 20, 20, 20, x, y, BLOCK_WIDTH,
                                                              BLOCK_HEIGHT);
            break;
        case 'block':
            ctx.drawImage(gfx, 0, 20, 20, 20, x, y, BLOCK_WIDTH,
                                                              BLOCK_HEIGHT);

            break;
    }
}

function drawIndicator(value) {
    var i, num = new String(value).split("");
    if (num.length > 8)
        throw "Indicator overflow";
    for (i = 0; i < 8; i++) {
        num[i] = parseInt(num[i], 10);
        drawSprite(num[i] >= 0 ? num[i]:'empty_digit',
                                               (i*14)+FIELD_OFFSET_X+84, 2);
    }
}

function drawField() {
	iterateOver(field, function(item, row, col) {
		drawSprite(item == X ? 'block' : 'empty_block',
			BLOCK_WIDTH * (col-1) + FIELD_OFFSET_X,
			BLOCK_HEIGHT * row + FIELD_OFFSET_Y
		);
	},[0,-1],[1,-1]);
}

function clearBlock() {
	drawBlock('empty_block');
}

function drawBlock() {
	var spriteName=arguments.length==1?arguments[0]:'block';
	iterateOver(tetrominos[tetromino.name][tetromino.rotation], function(item, row,col) {
		if(item == X) {
			drawSprite(spriteName, 
				(col*BLOCK_WIDTH)+(tetromino.x*BLOCK_WIDTH)+FIELD_OFFSET_X,
				(row*BLOCK_HEIGHT)+(tetromino.y*BLOCK_HEIGHT)+FIELD_OFFSET_Y
			);
		}
	});
}

function rotate(state, positions, times) {
	state += times;
	positions--;
	if(state > positions) {
		state = state % (positions+1)
	} else if(state <= 0) {
		state = (state % (positions+1))+positions;
	}
	return state;
}

function colision(block, x, y) {
	var coliding=false;
	iterateOver(block, function(item, row, col) {
		if(block[row][col] == X && field[y+row][x+1+col] == X) {
      coliding = true;
      return BREAK;
		}
	});
	return coliding;
}

function init() {
  canvas = document.getElementById('screen');
  gfx = document.getElementById("gfx");
  if(canvas.getContext) {
    ctx = canvas.getContext('2d');
  } else {
    throw "canvas is not supported";
  }
  ctx.fillStyle = "rgb(240,240,240)";
  ctx.fillRect (0, 0, BLOCK_WIDTH*10+FIELD_OFFSET_X*2, BLOCK_HEIGHT*20+FIELD_OFFSET_Y*2);
  ctx.beginPath();
  ctx.strokeStyle = "rgb(0,0, 0);";
  ctx.moveTo(0, 30);
  ctx.lineTo(400, 30);
  ctx.closePath();
  ctx.stroke();
  drawIndicator(points);
  drawField();
  chooseRandomTetromino();
}

function chooseRandomTetromino() {
	tetromino.name=[O,Z,S,I,J,L,T][Math.floor(Math.random()*6)];
  tetromino.rotation= Math.floor(Math.random()*(tetrominos[tetromino.name].length - 1));
	tetromino.x=Math.floor((FIELD_WIDTH - tetrominos[tetromino.name][tetromino.rotation][0].length) / 2);
  tetromino.y=0;
  if(colision(tetrominos[tetromino.name][tetromino.rotation],tetromino.x, tetromino.y)) {
    game=GS_OFF;
    mergeWithField();
    gameOver();
  } else {
    setSpeed(speed);
    if(game & GS_ON) {
      drawBlock();
    }
  }
}

function mergeWithField() {
  iterateOver(tetrominos[tetromino.name][tetromino.rotation], function(item, row, col) {
    if(item == X) {
      field[tetromino.y+row][tetromino.x+col+1]=X;
    }
  });
}

function tick() {
  if(game & GS_ON) {
    if(colision(tetrominos[tetromino.name][tetromino.rotation],tetromino.x, tetromino.y+1) == false) {
      clearBlock();
      tetromino.y++;
      drawBlock();
    } else {
      mergeWithField();
      var linesDroped = dropFullLines();
      if(linesDroped > 0) {
        // give points
        points += increasePointsAnimation(POINTS_FOR_DROPED_LINES[linesDroped]);

        // update speed acording to currrent points value
        speed = 0;
        while(LEVEL_TRESHOLDS[speed++] < points);
      }
      drawField();
      chooseRandomTetromino();
      return;
    }
  }
}

function increasePointsAnimation(p) {
  var interval;
  interval =  window.setInterval(function() {
    if(p <= 0) {
      window.clearInterval(interval);
    }
    if(p > 11) {
      p -= 11;
      drawIndicator(points-p);
    } else if(p > 0) {
      p--;
      drawIndicator(points-p);
    }
  },11);
  return p;
}

function gameOver() {
  var row = FIELD_HEIGHT, interval;
  game = GS_OFF;
  interval =  window.setInterval(function() {
    row--;
    if(row <= 0) {
      window.clearInterval(interval);
      interval =  window.setInterval(function() {
        if(row > FIELD_HEIGHT) {
          window.clearInterval(interval);
          resetField();
        }
        if(row < FIELD_HEIGHT) {
          for(i=1;i<=FIELD_WIDTH; i++) {
            field[row][i] = o;
          }
        }
        row++;
        drawField();
      }, 75);
    }
    for(i=1;i<=FIELD_WIDTH; i++) {
      field[row][i] = X;
    }
    drawField();
  }, 75);
}

var interval = null
function setSpeed(speed) {
  if(interval) {
    window.clearInterval(interval);
  }
  interval = window.setInterval(tick, (MAX_SPEED - speed) * 75);
}

document.onkeydown = function(e) {
  var pressedKey, rotation,rotationDirection;
  if (window.event)    { e = window.event; }
  if (e.which) { pressedKey = e.which; }
  if (e.keyCode)    { pressedKey = e.keyCode; }

  if(game & GS_ON) {
    clearBlock();
    switch(pressedKey) {
		  case 37: // left
        if(colision(tetrominos[tetromino.name][tetromino.rotation],tetromino.x-1,tetromino.y) == false) {
          tetromino.x--;
        }
        break;
      case 39: // right
        if(colision(tetrominos[tetromino.name][tetromino.rotation],tetromino.x+1,tetromino.y) == false) {
          tetromino.x++;
        }
        break;
      case 40 && game & GS_ON: // down
        if(colision(tetrominos[tetromino.name][tetromino.rotation],tetromino.x,tetromino.y+1) == false) {
          tetromino.y++;
        }
			  break;
      case 32: // space
        setSpeed(MAX_SPEED*3);
			  break;
		  case 38: //up
			  rotationDirection=-1;
		  case 13: // enter
        rotationDirection=rotationDirection == false?1:rotationDirection;
        rotation = rotate(
          tetromino.rotation,
          tetrominos[tetromino.name].length,
          1 // -1 for counterclocwise rotation
        );
        if(colision(tetrominos[tetromino.name][rotation],tetromino.x,tetromino.y) == false) {
          tetromino.rotation = rotation;
        } else {
          // try move one position and rotate
          if(colision(tetrominos[tetromino.name][rotation],tetromino.x-1,tetromino.y) == false) {
            tetromino.rotation = rotation;
            tetromino.x--;
          } else {
            if(colision(tetrominos[tetromino.name][rotation],tetromino.x+1,tetromino.y) == false) {
              tetromino.rotation = rotation;
              tetromino.x++;
            }
          }
        }
        rotationDirection=false; // reset for next rotation
			  break;
    }
	  drawBlock();
  } else {
    switch(pressedKey) {
      case 80: // p
        break;
      case 49: // 1 - start game A
        if((game & GS_ON) == false) {
          resetField();
          points=0;
          drawIndicator(points);
          setSpeed(1);
          game |= GS_ON;
        }
        break;
      default:
      // console.log(pressedKey);
	  }
  }
}

